/********************************************************************************
 * Copyright (c)2016-2020, ShenZhen WanJiaAn Technology CO.,LTD.
 * All Rights Reserved.
 *
 * File Name     : PowerManager.c
 * Author        : joki.zhu
 * Date          : 2019-10-23
 *
 * Record        :
 * 1.Date        : 2019-10-23
 *   Author      : joki.zhu
 *   Modification: Created file
 ********************************************************************************/
#include "PowerManager.h"
#include "ProductManager.h"
#include "DevGpio.h"
#include "DevLed.h"
#include "hi_uartmsg.h"
#include "System.h"
#include "DevSd.h"
#include "protocol.h"
#include "DevSocSub.h"
#ifdef YD_PROTOCOL
#include "DOTHejiaguhua.h"
#include "DOTSdkAPI.h"
#include "dot_api.h"
#endif

#define WAKELOCK_NUM            (72)
#define POWERSLEEP_TIME_SEC     (30)
#define WDT_PERIOD_TIME_SEC     (12)

typedef struct {
    char name[32];
    char bLock;
}POWER_WAKE_LOCK_S;

typedef struct {
    AV_CHAR bLocked;
    pthread_t powerProcPid;
    AV_BOOL bSleepEnable;
    AV_BOOL bSleeping;
    AV_INT sleepTime;
    AV_INT runTime;
    POWER_WAKE_LOCK_S astWakeLock[WAKELOCK_NUM];
}POWER_MNG_CONTEXT_S;

static POWER_MNG_CONTEXT_S g_powerMngCtx = {
    .bLocked            = 0,
    .powerProcPid       = 0,
    .bSleepEnable       = AV_TRUE,
    .bSleeping          = AV_FALSE,
    .sleepTime          = POWERSLEEP_TIME_SEC,
    .runTime            = 0,
    .astWakeLock        = {0},
};

static pthread_mutex_t gs_SocSleepMutex= PTHREAD_MUTEX_INITIALIZER;

static inline POWER_MNG_CONTEXT_S *powermng_get_ctx(void)
{
    return &g_powerMngCtx;
}


/**
 * @brief 电源上锁
 *
 * @param[in] name 锁名称
 * 
 * @return |0|成功| |非0|失败|
 *
 * @attention 设置电源上锁后,只有释放锁后,主控才能正常下电休眠
 */
int PowerSetWakeLock(char *name)
{
    AV_RETURN_IF_PTR_NULL(name, AV_FAILURE);

    int i = 0;
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    for (i = 0; i < WAKELOCK_NUM; i++)
    {
        if (strcmp(ctx->astWakeLock[i].name, name) == 0) {
            ctx->astWakeLock[i].bLock = 1;
            LOGI("%s WakeLock\n", name);
            break;
        }
    }

    if (i == WAKELOCK_NUM)
    {
        for (i = 0; i < WAKELOCK_NUM; i++)
        {
            if (ctx->astWakeLock[i].bLock == 0) {
                snprintf(ctx->astWakeLock[i].name, sizeof(ctx->astWakeLock[i].name), "%s", name);
                ctx->astWakeLock[i].bLock = 1;
                LOGI("%s WakeLock\n", name);
                break;
            }
        }

        if (i == WAKELOCK_NUM) {
            LOGW("power wakelock is full\n");
            return -1;
        }
    }
    
    return 0;
}

/**
 * @brief 电源下锁
 *
 * @param[in] name 锁名称
 * 
 * @return |0|成功| |非0|失败|
 *
 * @attention 设置电源上锁后,只有释放锁后,主控才能正常下电休眠
 */
int PowerSetWakeUnLock(char *name)
{
    AV_RETURN_IF_PTR_NULL(name, AV_FAILURE);

    int i = 0;
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    for (i = 0; i < WAKELOCK_NUM; i++)
    {
        if (ctx->astWakeLock[i].bLock == 1)
        {
            if (strcmp(ctx->astWakeLock[i].name, name) == 0) {
                ctx->astWakeLock[i].bLock = 0;
                memset(ctx->astWakeLock[i].name, 0, sizeof(ctx->astWakeLock[i].name));
                LOGI("%s WakeUnLock\n", name);
                break;
            }
            LOGI("%s is wake locked\n", ctx->astWakeLock[i].name);
        }

    }
    
    if (i == WAKELOCK_NUM) {
        LOGD("power wakelock is alredy unlocked (%s)\n", name);
    }

    return 0;
}

/**
 * @brief 设置主控电源维活开关及维活时间
 *
 * @param[in] bEnable 电源维活使能
 * @param[in] sleepTime 电源维活时间
 * 
 * @return |0|成功| |非0|失败|
 *
 * @attention 无
 */
int PowerSetSocSleepTime(AV_BOOL bEnable, AV_INT sleepTime)
{
    if (UpgradeCheckIsRun()) {
        return 0;
    }
    
	LOGI("bEnable:%d sleepTime:%d\n", bEnable, sleepTime);
	
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    AV_MUTEX_LOCK(gs_SocSleepMutex);
    ctx->bSleepEnable = bEnable;
    ctx->sleepTime = sleepTime;
    ctx->runTime = 0;
    AV_MUTEX_UNLOCK(gs_SocSleepMutex);
    
    return 0;
}

int PowerCheckIsLocked(void)
{
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();

    if (ctx->bLocked) {
        return 1;
    }

    return 0;
}

int PowerCheckIsSleeping(void)
{
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    return (ctx->bSleeping == AV_TRUE) ? 1 : 0;
}

//关机前预处理
static int poweroff_pre_process()
{
    int ret = 0;
    int pirState = 0;
    
    /* set pir state */
    ret = DevConfigGetPirSwitch(&pirState);
    if(ret != 0)
    {
        SocSubSetPirSwitch(1);//default
    }
    else
    {
        if(0 == pirState || 1 == pirState){
            SocSubSetPirSwitch(pirState);
        }else{
            SocSubSetPirSwitch(1);//default
        }
    }

    /* free protocol resource */
    ProtocalSleepFreeResource();

    /* close mcu wdt */
#ifdef SOCSUB_WDG_ENABLE
#if 0
    ret = SocSubSetWdgState(0);
    if (ret < 0) {
        LOGE("SocSubSetWdgState failed\n");
    }
#endif
#endif /* SOCSUB_WDG_ENABLE */	

#ifdef YD_PROTOCOL
	int waitCnt = 0;

	DOTStopSDK();
	while (1)
	{
		if ((!DOTGetSDKStatus()) || waitCnt > 5)	
		{
			break;
		}
		waitCnt++;
		usleep(100 * 1000);
	}
#endif

    return 0;
}

/**
 * @brief 设置主控关机休眠
 *
 * @param 无
 * 
 * @return |0|成功| |非0|失败|
 *
 * @attention 无
 */
int PowerSetSocPowerOff(void)
{
#ifdef SLEEP_MODE
    int i = 0;
    int ret = 0;
    char bPowerOff = 1;
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    /* check wakelock */
    for (i = 0; i < WAKELOCK_NUM; i++)
    {
        if (ctx->astWakeLock[i].bLock == 1) {
            LOGW("%s is wake locked\n", ctx->astWakeLock[i].name);
            bPowerOff = 0;
        }
    }

    if (bPowerOff == 1)
    {	
        ctx->bSleeping = AV_TRUE;
        
        /* 关机前预处理 */
        poweroff_pre_process();
        
        if (WifiCheckIsExistentConfig() == 1 && WifiCheckIsConnected() == 1) {//若WiFi配置存在且处于连接状态
            /* open wifi sleep mode */
            ret = WifiSetSleepMode();
#ifndef HISYSLINK_ENABLE
            if (ret == -1) { // create sleep heartbeat failed
                DevConfigSetWifiSleepMode(0);
                SocSubSetWifiPowerOff();
            } else if (ret == -2) { // find upgrade is running,stop sleep mode
                DevConfigSetWifiSleepMode(0);
                return -1;
            } else { // create sleep heartbeat success
                DevConfigSetWifiSleepMode(1);
            }
        } else {
            DevConfigSetWifiSleepMode(0);
            /* close wifi sleep mode */
            SocSubSetWifiPowerOff();
#endif /* HISYSLINK_ENABLE */
        }

        ret = SocSubSetSystemMode(HI_SYSLINK_SYS_MODE_LONGALIVE_DISABLE);
        if (ret != 0) {
            LOGE("SocSubSetSystemMode failed\n");
            return -1;
        }
        ret = SocSubSetMainSocPowerOff();
        if (ret != 0) {
            LOGE("SocSubSetMainSocPowerOff failed\n");
            return -1;
        }
        SocSubSetResetSwitch(0);
        LOGI(">>>>>>>>>> System will enter sleep mode !!! <<<<<<<<<<\n");

        //出现3次睡眠，就复位WiFi
        sleep(1);
        static char enterSleepFailCnt = 0;
        if (++enterSleepFailCnt >= 3) {
            SocSubSetResetSwitch(1);
            wifisoc_set_reset("system sleep failed");
        }
    }
    else 
    {
        return -1;
    }
#endif /* SLEEP_MODE */

    return 0;
}

void *tskPowerProc(void *args)
{
    SET_THREAD_NAME("tskPowerProc");
    pthread_detach(pthread_self());

    int ret = 0;
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();

    SocSubSetSystemMode(HI_SYSLINK_SYS_MODE_LONGALIVE_ENALBE);
    
    while (1)
    {
        AV_MUTEX_LOCK(gs_SocSleepMutex);
        
        ctx->bLocked = 1;
#ifdef YD_PROTOCOL
        static int livePrintCnt = 0;
        if (DOTCheckIsLivePreview()) {
            if (livePrintCnt == 0 || livePrintCnt%10 == 0) {
                LOGI("live preview is running...%ds\n", livePrintCnt);
            }
            ctx->bSleepEnable = AV_TRUE;
            ctx->sleepTime = 15; //预留缓冲睡眠时间,提升体验性
            ctx->runTime = 0;
            livePrintCnt++;
            goto sleep_fail;
        } else {
            livePrintCnt = 0;
        }
#endif /* YD_PROTOCOL */
    
        LOGD("system to sleepTime: %d, runTime: %d\n", ctx->sleepTime, ctx->runTime);

        if (ctx->bSleepEnable == AV_TRUE && ctx->runTime >= ctx->sleepTime)
        {
            ret = PowerSetSocPowerOff();
            if (ret < 0) {
                ctx->runTime = 0;
                ctx->sleepTime = POWERSLEEP_TIME_SEC;
                goto sleep_fail;
            }
        }
        ctx->runTime++;

 sleep_fail:
        AV_MUTEX_UNLOCK(gs_SocSleepMutex);
        ctx->bLocked = 0;
        sleep(1);
    }

    pthread_exit(0);
}

#ifdef BATTERY_TEST
void *tskBatteryTest(void *args)
{
    SET_THREAD_NAME("tskBatteryTest");
    pthread_detach(pthread_self());

    int ret = 0;
    char *fp = NULL;
    SYSTEM_TIME sysTime;
    char fileName[] = "/sdcard/b1_battery.txt";
    BATTERY_STATUS_S batteryStatus;
    char dataBuf[128] = "";
    
    while (1)
    {
        if (access(SDCARD_MOUNT_PATH, F_OK) == 0)
        {
            if (fp == NULL)
            {
                fp = fopen(fileName, "a");
                if (fp == NULL) {
                    LOGE("fopen %s failed\n", fileName);
                    break;
                }
            }
            
            memset(&batteryStatus, 0, sizeof(batteryStatus));
            ret = SocSubGetBatteryStatus(&batteryStatus);
            if (ret < 0) {
                LOGE("SocSubGetBatteryStatus failed\n");
                break;
            }

            memset(&sysTime, 0, sizeof(sysTime));
            SystemGetCurrentTime(&sysTime);

            memset(dataBuf, 0, sizeof(dataBuf));
            snprintf(dataBuf, sizeof(dataBuf), "%04d-%02d-%02d %02d:%02d:%02d battery value: %d\n", 
                                                sysTime.year, sysTime.month, sysTime.day, 
                                                sysTime.hour, sysTime.minute, sysTime.second,
                                                batteryStatus.capacity);
            LOGI("%s\n", dataBuf);
            
            fwrite(dataBuf, strlen(dataBuf)+1, 1, fp);
            fflush(fp);
            
            sleep(60);
        }
        else
        {
            sleep(1);
        }
    }

    if (fp != NULL) {
        fflush(fp);
        fclose(fp);
    }
    pthread_exit(0);
}
#endif /* BATTERY_TEST */

int PowerInit(void)
{
    int ret = 0;
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    ret = pthread_create(&ctx->powerProcPid, 0, (void *)tskPowerProc, NULL);
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, AV_FAILURE, "pthread_create tskPowerProc");


#ifdef BATTERY_TEST
    pthread_t batteryTestPid;
    ret = pthread_create(&batteryTestPid, 0, (void *)tskBatteryTest, NULL);
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, AV_FAILURE, "pthread_create tskBatteryTest");
#endif /* BATTERY_TEST */

    return 0;
}


int PowerDeinit(void)
{
    POWER_MNG_CONTEXT_S *ctx = powermng_get_ctx();
    
    SocSubDeinit();
    pthread_cancel(ctx->powerProcPid);

    return 0;
}

